﻿using System;
using System.Configuration;
using System.Collections.Generic;
using System.Data;
using System.Windows.Forms;
using System.Xml;
using SdmxMl.Helper;
using SdmxMl.Common;

namespace SdmxMl.Structure
{
    /// <summary>
    /// ProcessStepType describes a single step in a statistical process.
    /// ProcessSteps may be recursive. The Input element specifies the type of
    /// object(s) which serve as inputs to the process; the Output element specifies
    /// the type of objects which are the result of the process. Computation elements
    /// describe the computations involved in the process, in any form desired by the
    /// user (these are informational rather than machine-actionable), and so may be
    /// supplied in multiple, parallel-language versions. Transitions describe the
    /// process steps to which a process is connected - that is, which processes
    /// happen next. The process step maust be given a Name, and may be given a
    /// Description. These are human-readable, and may be supplied in multiple,
    /// parallel-language versions. Annotations may be supplied. The id attribute
    /// takes the unique identifier of the process step within the parent process.
    /// </summary>
    public class ProcessStepType : BaseArtefact, ISdmxHierarchyListItem
    {
        #region Properties

        private MultiLang comput;
        public MultiLang Computation
        {
            get { return comput; }
            set { comput = value; }
        }

        public string FullId
        {
            get
            {
                ProcessStepType c = this;
                string i = Id;
                /*
                while (c.ParentStep != null)
                {
                    i = c.ParentStep.id + "-" + i;
                    c = c.ParentStep;
                }
                 * */
                return i;
            }
        }

        private ObjectIDTypeList _inputList;
        private ObjectIDTypeList _outputList;

        public ObjectIDTypeList InputList
        {
            get { return _inputList; }
        }

        public ObjectIDTypeList OutputList
        {
            get { return _outputList; }
        }

        private TransitionTypeList _transitionList;
        public TransitionTypeList TransitionList
        {
            get { return _transitionList; }
        }

        private ProcessStepType _parentStep;
        public ProcessStepType ParentStep
        {
            get { return _parentStep; }
            set { _parentStep = value; }
        }

        public NestedHierarchicalList RootHierarchy { get; set; }

        private ProcessStepTypeList _processStepList;
        public ProcessStepTypeList ProcessStepList
        {
            get { return _processStepList; }
        }

        public IHierarchyManage InnerHierarchy
        {
            get { return _processStepList; }
        }

        // For ISdmxHierarchyListItem compatibility
        public string Parent
        {
            get
            {
                return ParentStep != null ? ParentStep.Id : string.Empty;
            }
            set { }
        }

        #endregion //Properties

        #region Constructors
        
        private void Init()
        {
            comput = new MultiLang();
            _inputList = new ObjectIDTypeList();
            _outputList = new ObjectIDTypeList();
            _transitionList = new TransitionTypeList();
            _processStepList = new ProcessStepTypeList(RootHierarchy);

        }


        public ProcessStepType(NestedHierarchicalList root, ProcessStepType parentStp)
        {
            RootHierarchy = root;
            _parentStep = parentStp;
            Init();

        }

        public ProcessStepType(NestedHierarchicalList root, ProcessStepType parentStp, XmlNode nodeArtefact, SdmxNamespaceManager nsmgr) 
            : base(nodeArtefact, nsmgr)
        {
            RootHierarchy = root;
            _parentStep = parentStp;
            Init();
    Id = Id.Replace(".", "_");
            FillProcessStepList(_processStepList, nodeArtefact, nsmgr);
        }

        private void FillProcessStepList(ProcessStepTypeList psl, XmlNode nodeArtefact, SdmxNamespaceManager nsmgr)
        {
            MultiLangNodeReader mr = new MultiLangNodeReader();
            mr.Read(comput, nodeArtefact, "Computation", nsmgr, false);

            
            XmlNodeList nodeList = nodeArtefact.SelectNodes("./structure:Input", nsmgr);
            foreach (XmlNode nd in nodeList)
            {
                string str = nd.InnerText;
                if (Enum.IsDefined(typeof(ObjectIDType), str))
                    InputList.Add( (ObjectIDType)Enum.Parse(typeof(ObjectIDType), str, true) );
                else
                    throw new ApplicationException("Unrecognised Input object in Process Step: " + str);
            }

            nodeList = nodeArtefact.SelectNodes("./structure:Output", nsmgr);
            foreach (XmlNode nd in nodeList)
            {
                string str = nd.InnerText;
                if (Enum.IsDefined(typeof(ObjectIDType), str))
                    OutputList.Add((ObjectIDType)Enum.Parse(typeof(ObjectIDType), str, true));
                else
                    throw new ApplicationException("Unrecognised Output object in Process Step: " + str);
            }

            nodeList = nodeArtefact.SelectNodes("./structure:Transition", nsmgr);
            foreach (XmlNode nd in nodeList)
            {
                TransitionType df = new TransitionType(nd, nsmgr);
                _transitionList.Add(df);
            }
            
            string query = "./structure:ProcessStep";
            nodeList = nodeArtefact.SelectNodes(query, nsmgr);
            if (nodeList.Count > 0)
            {
                foreach (XmlNode nd in nodeList)
                {
                    ProcessStepType c = new ProcessStepType(RootHierarchy, this, nd, nsmgr);
                    psl.Add(c);
                }
            }
        }


        #endregion // Constructors

        #region Clone

        public ISdmxHierarchyListItem Clone()
        {
            ProcessStepType c = new ProcessStepType(null, null);
            c.Id = Id;
            c.Name = Name.Clone();
            c.Description = Description.Clone();

            c.AnnotationList = AnnotationList.Clone();

            return c;
        }
        #endregion

        #region XML serialize

        public void WriteXml(XmlTextWriter xw, string prefix, string tag)
        {
            xw.WriteStartElement(prefix, tag, null);
            // id required
            if (string.IsNullOrEmpty(Id))
                Id = "undefined";
            xw.WriteAttributeString("id", Id);

            Name.WriteXml(xw, prefix, "Name");
            Description.WriteXml(xw, prefix, "Description");

            // All objects ref
            foreach (ObjectIDType df in InputList)
                Xmlhelper.WriteElementIfExist(xw, prefix, "Input", df.ToString());
            foreach (ObjectIDType df in OutputList)
                Xmlhelper.WriteElementIfExist(xw, prefix, "Output", df.ToString());

            Computation.WriteXml(xw, prefix, "Computation");

            foreach (TransitionType t in _transitionList)
                t.WriteXml(xw, prefix, "Transition");

            foreach (ProcessStepType step in _processStepList)
                step.WriteXml(xw, prefix, tag);


            // Annotations
            AnnotationList.WriteXml(xw, prefix, null);

            xw.WriteEndElement();
        }

        #endregion

        #region Tree

        public override TreeNode FillNode(TreeNode parentNode)
        {
            TreeNode t = base.FillNode(parentNode);
            t.ImageIndex = t.SelectedImageIndex = 5;

            // Specific OFS activities
            string icoCode = this.AnnotationList.GetTextOfAnnotationType("SMS_TYPE");
            if (icoCode.Length > 0)
            {
                char icodeChar = icoCode[0];
                if (icodeChar == 'A')
                    t.ImageIndex = t.SelectedImageIndex = 6;
                else if (icodeChar == 'F')
                    t.ImageIndex = t.SelectedImageIndex = 12;
                else if (icodeChar == 'W')
                    t.ImageIndex = t.SelectedImageIndex = 7;
                else if (icodeChar == 'I')
                    t.ImageIndex = t.SelectedImageIndex = 10;
                else if (icodeChar == 'Q')
                    t.ImageIndex = t.SelectedImageIndex = 9;
                else if (icodeChar == 'P')
                    t.ImageIndex = t.SelectedImageIndex = 8;
                else if (icodeChar == 'S')
                    t.ImageIndex = t.SelectedImageIndex = 11;
                else if (icodeChar == 'M')
                    t.ImageIndex = t.SelectedImageIndex = 13;
                else if (icodeChar == 'B')
                    t.ImageIndex = t.SelectedImageIndex = 16;
                else if (icodeChar == 'N')
                    t.ImageIndex = t.SelectedImageIndex = 15;
                else if (icodeChar == 'V')
                    t.ImageIndex = t.SelectedImageIndex = 17;
                else if (icodeChar == 'H')
                    t.ImageIndex = t.SelectedImageIndex = 14;
            }

            foreach (ProcessStepType c in _processStepList)
                c.FillNode(t);
            return t;
        }

        #endregion // Tree

        #region String Representation

        public override string ToString()
        {
            return FullId + " - " + Name.ToString(); ;
        }
        #endregion // String Representation

    }


    /// <summary>
    /// Holds a list of category in a certain level of a category Scheme.
    /// </summary>
    public class ProcessStepTypeList : NestedHierarchicalList
    {
        public ProcessStepTypeList(NestedHierarchicalList root)
            : base(root)
        {
        }

        public override ISdmxHierarchyListItem GetParent(ISdmxHierarchyListItem it)
        {
            ProcessStepType step = it as ProcessStepType;
            if (step != null)
                return step.ParentStep;
            return null;
        }

        public override ISdmxHierarchyListItem CreateNewItem(ISdmxHierarchyListItem parentItem)
        {
            ProcessStepType parentStep = parentItem as ProcessStepType;
            ProcessStepType step = new ProcessStepType(this, parentStep);
            return step;
        }

        public IHierarchyManage GetHierarchyManageOfItem(ProcessStepType step)
        {
            if (step.ParentStep == null)
                return rootHierarchy;
            return step.ParentStep.InnerHierarchy;
        }

        public override string ToString()
        {
            return "ProcessStep";
        }

        protected override void FillTableRepresentation(NestedHierarchicalList cur, DataTable dataTable)
        {
            NestedHierarchicalList nhl = cur == null ? rootHierarchy : cur;
            foreach (ProcessStepType it in nhl)
            {
                dataTable.Rows.Add(new object[] { it.FullId, it.Name });
                FillTableRepresentation((it.InnerHierarchy as NestedHierarchicalList), dataTable);
            }
        }

        /// <summary>
        /// Override to place full ID as information. Because ID can be same at differents levels.
        /// </summary>
        /// <param name="cur"></param>
        /// <param name="dataTable"></param>
        /// <param name="item"></param>
        protected override void FillPossibleParent(NestedHierarchicalList cur, DataTable dataTable, ISdmxHierarchyListItem item)
        {
            NestedHierarchicalList nhl = cur == null ? rootHierarchy : cur;
            foreach (ProcessStepType it in nhl)
            {
                if (item != it)
                {
                    dataTable.Rows.Add(new object[] { it.FullId, it.Name, it });
                    FillPossibleParent((it.InnerHierarchy as NestedHierarchicalList), dataTable, item);
                }
            }
        }

        public ProcessStepType LocateItemFromFullId(string fullId, NestedHierarchicalList nhl)
        {
            ProcessStepType ret = null;

            foreach (ProcessStepType cat in nhl)
            {
                if (cat.FullId == fullId)
                {
                    ret = cat;
                    break;
                }
                else
                {
                    ProcessStepType rep = LocateItemFromFullId(fullId, cat.InnerHierarchy as NestedHierarchicalList);
                    if (rep != null)
                        ret = rep;
                }
            }
            return ret;
        }

        public override void RedefineParent(ISdmxHierarchyListItem item, string newParentId, object newOwner)
        {
            NestedHierarchicalList oldParent = LocateOwner(rootHierarchy, (item as ProcessStepType), false);
            NestedHierarchicalList newParent = null;
            ProcessStepType newParentCat = newOwner as ProcessStepType;

            if (newOwner != null)
                newParent = LocateOwner(rootHierarchy, newParentCat, true);
            else
                newParent = rootHierarchy;

            if (oldParent != null && newParent != null && oldParent != newParent)
            {
                oldParent.Remove(item);
                newParent.Add(item);
                foreach (ProcessStepType cat in newParent)
                    cat.ParentStep = newParentCat;
            }
        }

        private NestedHierarchicalList LocateOwner(NestedHierarchicalList nhl, ProcessStepType ps, bool getInnerHierarchy)
        {
            NestedHierarchicalList loc = null;
            NestedHierarchicalList cur = null;
            foreach (ProcessStepType it in nhl)
            {
                if (it == ps)
                {
                    if (getInnerHierarchy == false)
                        loc = nhl;
                    else
                        loc = it.InnerHierarchy as NestedHierarchicalList;
                    break;
                }
                else
                {
                    cur = LocateOwner((it.InnerHierarchy as NestedHierarchicalList), ps, getInnerHierarchy);
                    if (cur != null)
                        loc = cur;
                }
            }
            return loc;
        }
    }

}
